﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using MindFusion.Diagramming;
using System.Xml.Serialization;
using System.IO;
using Semantic;
using NetSettings;
using System.Diagnostics;

namespace WindowsFormsApplication1
{
    public partial class FormMain : Form
    {
        bool diagramSaved = true;
        public Settings _Settings;
        string[] types = new string[] { "is_a", "a_part_of", "name", "element_of", "restriction", "use", "subtopic", "precede" };

        public FormMain()
        {
            InitializeComponent();
            ReadSettings();
        }

        private void ReadSettings()
        {
            try
            {
                var reader = new StreamReader("settings.xml");
                var xmlSerializer = new XmlSerializer(typeof(Settings));
                _Settings = xmlSerializer.Deserialize(reader) as Settings;
                reader.Close();
            }
            catch
            {
                MessageBox.Show(@"Не найден файл настроек 'settings.xml'!");
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            diagramView.Behavior = Behavior.DrawLinks;
            diagram.DefaultShape = Shapes.Ellipse;
            diagram.DrawNode += new DrawNodeEventHandler(diagram1_DrawNode);
            diagram.LinkCreated += new LinkEventHandler(diagram1_LinkCreated);
            diagramView.MouseWheel += new MouseEventHandler(diagramView_MouseWheel);
            diagram.UndoManager.UndoEnabled = true;
            undoToolStripButton.Enabled = false;
            redoToolStripButton.Enabled = false;
        }

        #region Взаимодействие с элементами диаграммы

        TextBox txtName;
        ComboBox cmbType;
        DataGridView gridSynset;
        PointF pt = new PointF(-50, -50);
        SizeF szf10 = new SizeF(10, 10);
        SizeF szf20 = new SizeF(20, 20);
        Point gMousePosition;

        ShapeNode currentNode;

        private void ShowNameTextbox(ShapeNode node)
        {
            if (node.ToolTip == "named")
            {
                txtName = new TextBox();
                this.Controls.Add(txtName);
                txtName.TextAlign = HorizontalAlignment.Center;
                txtName.Location = Point.Add(diagramView.PointToClient(gMousePosition) + (new Size(-txtName.Width / 2, 10)), new Size(diagramView.Location));
                txtName.Text = node.Text;
                txtName.SelectAll();
                txtName.KeyDown += new KeyEventHandler(txtName_KeyDown);
                txtName.LostFocus += new EventHandler(txtName_LostFocus);
                txtName.BringToFront();
                txtName.Show();
                hintLabel.Text = "Для сохранения изменений нажмите Enter, для отмены - Esc.";
                txtName.Focus();
                currentNode = node;
            }
        }

        private void ShowTypeCombobox(DiagramLink link)
        {
            cmbType = new ComboBox();
            cmbType.DataSource = types;
            cmbType.DropDownStyle = ComboBoxStyle.DropDownList;
            this.Controls.Add(cmbType);
            if (diagramView.ZoomFactor == 100)
                cmbType.Location = this.PointToClient(diagramView.PointToScreen(Point.Ceiling(PointF.Add(link.GetBounds().Location, new SizeF(link.GetBounds().Width / 2, link.GetBounds().Height / 2)))));
            else
                cmbType.Location = Point.Add(diagramView.PointToClient(MousePosition), new Size(diagramView.Location));
            cmbType.Text = link.Text;
            cmbType.LostFocus += new EventHandler(txtName_LostFocus);
            cmbType.KeyDown += new KeyEventHandler(cmbType_KeyDown);
            cmbType.BringToFront();
            cmbType.Show();
            hintLabel.Text = "Для сохранения изменений нажмите Enter, для отмены - Esc.";
            cmbType.Focus();
        }

        private void ShowSynsetGrid(ShapeNode node)
        {
            if (node.ToolTip == "named")
            {
                gridSynset = new DataGridView();
                gridSynset.ColumnCount = 1;
                gridSynset.ColumnHeadersVisible = false;
                gridSynset.RowHeadersVisible = false;
                gridSynset.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
                gridSynset.BackgroundColor = Color.White;
                gridSynset.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
                gridSynset.Width = 200;
                this.Controls.Add(gridSynset);
                if (diagramView.ZoomFactor == 100)
                    gridSynset.Location = Point.Add(Point.Ceiling(node.GetCenter()), new Size(diagramView.Location));
                else
                    gridSynset.Location = Point.Add(diagramView.PointToClient(MousePosition), new Size(diagramView.Location));
                List<string> synset = node.Tag as List<string>;
                if (synset == null) synset = new List<string>();
                foreach (string s in synset)
                {
                    gridSynset.Rows.Add(s);
                }
                gridSynset.KeyDown += new KeyEventHandler(gridSynset_KeyDown);
                gridSynset.LostFocus += new EventHandler(gridSynset_LostFocus);
                gridSynset.CellLeave += new DataGridViewCellEventHandler(gridSynset_CellLeave);
                gridSynset.BringToFront();
                gridSynset.Show();
                hintLabel.Text = "Для сохранения изменений нажмите Enter, для отмены - Esc.";
                gridSynset.Focus();
                currentNode = node;
            }
        }

        void gridSynset_CellLeave(object sender, DataGridViewCellEventArgs e)
        {
            //            
            gridSynset.BeginEdit(false);
            gridSynset.EndEdit();
        }

        void gridSynset_LostFocus(object sender, EventArgs e)
        {
            if (!gridSynset.ContainsFocus)
            {
                gridSynset.Dispose();
                hintLabel.Text = "";
            }
        }

        void gridSynset_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                List<string> synset = new List<string>();
                foreach (DataGridViewRow r in gridSynset.Rows)
                {
                    if (r.Cells[0].Value != null) synset.Add(r.Cells[0].Value.ToString());
                }
                currentNode.Tag = synset;
                diagramSaved = false;
                (sender as Control).Dispose();
                diagramView.Refresh();
            }
            else if (e.KeyCode == Keys.Escape)
                (sender as Control).Dispose();
        }

        void cmbType_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                string type = diagram.Selection.Links[0].Text;
                diagram.Selection.Links[0].Text = (sender as ComboBox).Text;
                if (!Validation(diagram.Selection.Links[0]))
                {
                    diagram.UndoManager.Undo();
                    diagram.Selection.Links[0].Text = type;
                    ShowTypeCombobox(diagram.Selection.Links[0]);
                }
                diagramSaved = false;
                (sender as ComboBox).Dispose();
                diagramView.Refresh();
            }
            else if (e.KeyCode == Keys.Escape)
                (sender as ComboBox).Dispose();
        }

        void txtName_LostFocus(object sender, EventArgs e)
        {
            (sender as Control).Dispose();
            hintLabel.Text = "";
        }

        void txtName_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                foreach (ShapeNode node in diagram.Nodes)
                {
                    if (node.Text.Trim().ToLower() == (sender as TextBox).Text.Trim().ToLower())
                    {
                        MessageBox.Show("Вершина с таким именем уже существует!");
                        ShowNameTextbox(diagram.Selection.Nodes[0] as ShapeNode);
                        return;
                    }
                }
                currentNode.Text = (sender as TextBox).Text;
                diagramSaved = false;
                (sender as TextBox).Dispose();
            }
            else if (e.KeyCode == Keys.Escape)
                (sender as TextBox).Dispose();
        }

        void diagramView_MouseWheel(object sender, MouseEventArgs e)
        {
            if (e.Delta > 0) diagramView.ZoomIn();
            else if (e.Delta < 0) diagramView.ZoomOut();
        }

        void diagram1_DrawNode(object sender, DrawNodeEventArgs e)
        {
            System.Drawing.Drawing2D.GraphicsPath x =
               new System.Drawing.Drawing2D.GraphicsPath();

            x.AddEllipse(e.Bounds);

            Color surrounding;
            if (e.Node.ToolTip == "named") surrounding = Color.Blue;
            else surrounding = Color.Green;

            System.Drawing.Drawing2D.PathGradientBrush p =
               new System.Drawing.Drawing2D.PathGradientBrush(x);
            p.CenterColor = Color.White;
            p.SurroundColors = new Color[] { surrounding };

            e.Graphics.FillEllipse(p, e.Bounds);
            string s = (e.Node as ShapeNode).Text;
            e.Graphics.DrawString(s, Font, Brushes.Black, PointF.Add(e.Node.GetCenter(), new SizeF((float)(-3.5 * s.Length), 10)));

            p.Dispose();
            x.Dispose();

            System.Drawing.Pen pp = new System.Drawing.Pen(surrounding, 0);

            e.Graphics.DrawEllipse(pp, e.Bounds);

            pp.Dispose();
        }

        private void diagram_Clicked(object sender, DiagramEventArgs e)
        {
            pt = e.MousePosition;
            gMousePosition = MousePosition;
            diagram.Selection.Clear();
            diagramView.ContextMenuStrip = contextMenuDiagram;
            if (e.MouseButton == MouseButton.Right) diagramView.ContextMenuStrip.Show(MousePosition);
        }

        private void diagram_NodeClicked(object sender, NodeEventArgs e)
        {
            contextMenuNode.Items[0].Enabled = false;
            contextMenuNode.Items[1].Enabled = false;
            if (diagram.GetNodeAt(e.MousePosition, 20).ToolTip == "named")
            {
                contextMenuNode.Items[0].Enabled = true;
                contextMenuNode.Items[1].Enabled = true;
            }
            diagram.Selection.Clear();
            e.Node.Selected = true;
            diagramView.ContextMenuStrip = contextMenuNode;
            gMousePosition = MousePosition;
            if (e.MouseButton == MouseButton.Right) diagramView.ContextMenuStrip.Show(MousePosition);
        }

        private void diagram_LinkClicked(object sender, LinkEventArgs e)
        {
            diagram.Selection.Clear();
            e.Link.Selected = true;
            diagramView.ContextMenuStrip = contextMenuLink;
            gMousePosition = MousePosition;
            if (e.MouseButton == MouseButton.Right) diagramView.ContextMenuStrip.Show(MousePosition);
        }

        bool Validation(DiagramLink link)
        {
            DiagramLink linkToDelete = null;
            if (link.Text == "name" && link.Destination.ToolTip == "unnamed")
            {
                MessageBox.Show("Дуга типа 'name' может входить только в именованную вершину");
                linkToDelete = link;
            }
            if (link.Origin.ToolTip == "named")
            {
                MessageBox.Show("Из именованной вершины не может выходить дуг");
                linkToDelete = link;
            }
            if (link.Destination.ToolTip == "named" && link.Text != "name")
            {
                MessageBox.Show("В именованную вершину может входить только дуга типа 'name'");
                linkToDelete = link;
            }
            if (IsReachable(link.Destination, link.Destination))
            {
                MessageBox.Show("Добавление этой дуги образует цикл. Циклы в сем. сети не допускаются.");
                linkToDelete = link;
            }
            if (linkToDelete != null)
            {
                diagram.Links.Remove(linkToDelete);
                return false;
            }
            return true;
        }

        private bool IsReachable(DiagramNode node1, DiagramNode node2)
        {
            foreach (DiagramLink link in node1.OutgoingLinks)
            {
                if (link.Destination == node2) return true;
            }
            foreach (DiagramLink link in node1.OutgoingLinks)
            {
                if (IsReachable(link.Destination,node2)) return true;
            }
            return false;
        }

        private void diagramView_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Control && e.KeyCode == Keys.Z) diagram.UndoManager.Undo();
        }

        private void diagram_LinkModified(object sender, LinkEventArgs e)
        {
            diagramSaved = false;
            undoToolStripButton.Enabled = true;
        }

        private void diagram_NodeModified(object sender, NodeEventArgs e)
        {
            diagramSaved = false;
            undoToolStripButton.Enabled = true;
        }

        private void удалитьToolStripMenuItem_Click(object sender, EventArgs e)
        {
            diagram.Nodes.Remove(diagram.Selection.Nodes[0]);
            undoToolStripButton.Enabled = true;
        }

        private void удалитьToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            diagram.Links.Remove(diagram.Selection.Links[0]);
            undoToolStripButton.Enabled = true;
        }

        private void сменитьИмяToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DiagramNode node = diagram.Selection.Nodes[0];
            if (node != null) ShowNameTextbox(node as ShapeNode);
        }

        void diagram1_LinkCreated(object sender, LinkEventArgs e)
        {
            if (e.Link.Destination.ToolTip != "named")
                e.Link.Text = "is_a";
            else
                e.Link.Text = "name";
            if (e.Link.Origin.ToolTip == "named")
            {
                diagram.Links.Remove(e.Link);
                MessageBox.Show("Из именованной вершины не может выходить дуг");
            }
            else
            {
                ShowTypeCombobox(e.Link);
                Validation(e.Link);
                undoToolStripButton.Enabled = true;
            }
        }

        private void diagram_LinkSelected(object sender, LinkEventArgs e)
        {
            e.Link.Pen.Width = 3;
        }

        private void diagram_LinkDeselected(object sender, LinkEventArgs e)
        {
            e.Link.Pen.Width = 1;
            diagramView.ContextMenuStrip = null;
        }

        private void сменитьТипToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowTypeCombobox(diagram.Selection.Links[0]);
        }

        private void toolStripMenuItem1_Click(object sender, EventArgs e)
        {
            ShowSynsetGrid(diagram.Selection.Nodes[0] as ShapeNode);
        }

        private void создатьИменованнуюВершинуToolStripMenuItem_Click(object sender, EventArgs e)
        {
            pt.X += -10;
            pt.Y += -10;
            MindFusion.Diagramming.ShapeNode node = diagram.Factory.CreateShapeNode(pt, szf20);
            node.ToolTip = "named";
            node.Text = "Node" + diagram.Nodes.Count.ToString();
            node.CustomDraw = CustomDraw.Full;
            node.Brush = new MindFusion.Drawing.LinearGradientBrush(Color.White, Color.Green);
            node.Constraints.MaxHeight = node.Constraints.MinHeight = 20;
            node.Constraints.MaxWidth = node.Constraints.MinWidth = 20;
            diagramSaved = false;
            undoToolStripButton.Enabled = true;
            ShowNameTextbox(node);
        }

        private void создатьНеименованнуюВершинуToolStripMenuItem_Click(object sender, EventArgs e)
        {
            pt.X += -10;
            pt.Y += -10;
            MindFusion.Diagramming.ShapeNode node = diagram.Factory.CreateShapeNode(pt, szf20);
            node.ToolTip = "unnamed";
            node.CustomDraw = CustomDraw.Full;
            node.Brush = new MindFusion.Drawing.LinearGradientBrush(Color.White, Color.Green);
            node.Constraints.MaxHeight = node.Constraints.MinHeight = 20;
            node.Constraints.MaxWidth = node.Constraints.MinWidth = 20;
            diagramSaved = false;
            undoToolStripButton.Enabled = true;
            ShowNameTextbox(node);
        }

        private void diagram_NodeDeselected(object sender, NodeEventArgs e)
        {
            diagramView.ContextMenuStrip = null;
        }
        #endregion


        #region Взаимодействие с диаграммой в целом

        private void мЛВToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var setForm = new SettingsForm(_Settings, this);
            setForm.Show();
        }

        private void undoToolStripButton_Click(object sender, EventArgs e)
        {
            diagram.UndoManager.Undo();
            if (diagram.UndoManager.History.NextUndo == null)
                undoToolStripButton.Enabled = false;
            if (diagram.UndoManager.History.NextRedo != null)
                redoToolStripButton.Enabled = true;
        }

        private void redoToolStripButton_Click(object sender, EventArgs e)
        {
            diagram.UndoManager.Redo();
            if (diagram.UndoManager.History.NextRedo == null)
                redoToolStripButton.Enabled = false;
            if (diagram.UndoManager.History.NextUndo != null)
                undoToolStripButton.Enabled = true;
        }

        private void zoomOutToolStripButton_Click(object sender, EventArgs e)
        {
            diagramView.ZoomOut();
        }

        private void zoomInToolStripButton_Click(object sender, EventArgs e)
        {
            diagramView.ZoomIn();
        }

        private void справкаToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            CallHelp();
        }

        private void helpToolStripButton_Click(object sender, EventArgs e)
        {
            CallHelp();
        }

        bool CheckForSave()
        {
            if (!diagramSaved)
            {
                DialogResult dr = MessageBox.Show("Текущая диаграмма не сохранена! Хотите ее сохранить?", "", MessageBoxButtons.YesNoCancel);
                if (dr == DialogResult.Yes) btnSave_Click(diagram, new EventArgs());
                if (dr == DialogResult.Cancel) return false;
            }
            return true;
        }

        SemanticNetwork sn;
        private void Save(string path)
        {
            sn = Converter.Diagram2SemanticNet(diagram);
            XmlSerializer xs = new XmlSerializer(typeof(SemanticNetwork), new Type[] { typeof(NamedNode) });
            xs.Serialize(new StreamWriter(path), sn);
            diagram.Tag = path;
            diagram.SaveToXml(path + ".diagram");
            diagramSaved = true;
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            if (diagram.Tag != null)
                Save(diagram.Tag.ToString());
            else
                btnSaveAs_Click(sender, e);
        }

        private void btnSaveAs_Click(object sender, EventArgs e)
        {
            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                Save(saveFileDialog.FileName);
            }
        }

        private void btnLoad_Click(object sender, EventArgs e)
        {
            CheckForSave();
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                bool successful = true;
                try
                {
                    XmlSerializer xs = new XmlSerializer(typeof(SemanticNetwork), new Type[] { typeof(NamedNode) });
                    sn = xs.Deserialize(new StreamReader(openFileDialog.FileName)) as SemanticNetwork;
                    diagram.LoadFromXml(openFileDialog.FileName + ".diagram");
                    diagramSaved = true;
                }
                catch
                {
                    successful = false;
                }
                if (!successful)
                    try
                    {
                        //Semantic.SemanticNetwork sn;
                        diagram.LoadFromXml(openFileDialog.FileName);
                        diagramSaved = true;
                        successful = true;
                    }
                    catch
                    {
                        MessageBox.Show("Неверный XML файл");
                        successful = false;
                    }
                if (successful)
                {
                    diagram.Tag = openFileDialog.FileName;
                    for (int i = 0; i < diagram.Nodes.Count; i++)
                    {
                        if (string.IsNullOrEmpty(diagram.Nodes[i].ToolTip))
                        {
                            diagram.Nodes[i].ToolTip = diagram.Nodes[i].Tag.ToString();
                            diagram.Nodes[i].Tag = null;
                        }
                    }
                }
            }
        }

        private void btnNew_Click(object sender, EventArgs e)
        {
            CheckForSave();
            diagram.ClearAll();
            diagramView.Refresh();
        }

        private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (!CheckForSave()) e.Cancel = true;
        }

        private void выходToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }
        #endregion

        private void начатьКонсультациюToolStripMenuItem_Click(object sender, EventArgs e)
        {
            sn = Converter.Diagram2SemanticNet(diagram);
            var chat = new Chat(sn, this);
            chat.Show();
        }

        public void ShowExplaination(Explanation explanation)
        {
            diagram.Selection.Clear();
            HideExplaination();
            foreach (var g in explanation.Arcs)
            {
                diagram.FindLinkById(g).Pen.Color = Color.Red;
                diagram.FindLinkById(g).Pen.Width = 3;
            }
            diagramView.Refresh();
        }

        public void HideExplaination()
        {
            for (var i = 0; i < diagram.Links.Count; i++)
            {
                diagram.Links[i].Pen.Color = Color.Black;
                diagram.Links[i].Pen.Width = 1;
            }
            diagramView.Refresh();
        }

        public void UpdateSettings()
        {
            ReadSettings();
        }

        private void CallHelp()
        {
            var process = new Process {StartInfo = new ProcessStartInfo("help.chm")};
            process.Start();
        }

        private void оПрограммеToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var about = new About();
            about.Show();
        }
    }
}
